跳到主要内容

SpringBoot 整合日志系统

SLF4J 标准是啥?

参考资料 四.SpringBoot与日志配置 参考资料 Java日志框架:slf4j作用及其实现原理

一直已经就觉得 Java 的日志框架很多且乱,好奇是如何整合的

实际上一般的日志框架是通过一个 SLF4J 的标准整合的,使用它可以完美的桥接到具体的日志框架,必要时可以简便的更换底层的日志框架,而不需要关心具体的日志框架的实现(具体看下面)

所以平时使用 Lombok 就可以直接使用 @Slf4j 注解直接访问到 log 对象

import org.slf4j.Logger;

@SpringBootTest
@Slf4j
public class LoggerTest {
@Test
public void test(){
log.debug("debug");
log.info("info");
log.error("error");
log.warn("warn");
}
}

实际上面等效于

import org.slf4j.Logger;

@SpringBootTest
public class LoggerTest {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoggerTest.class);

@Test
public void test(){
log.debug("debug");
log.info("info");
log.error("error");
log.warn("warn");
}
}

slf4j 作用及其实现原理

SLF4J 英文全称是 Simple Logging Facade for Java, 是一个 Facet模式(外观模式)接口或者说各种日志框架的抽象,比如 java.util.logging, logback, log4j 等;使用这货,我们可以不用关心具体的实现,也就是说可以随时切换日志框架。

其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。

外观模式的核心为 Facade即外观对象,外观对象核心为几个点:

  • 知道所有子角色的功能和责任
  • 将客户端发来的请求委派到子系统中,没有实际业务逻辑
  • 不参与子系统内业务逻辑的实现

如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ ——它依赖于于另外一个日志类库 logback,那么你就需要把它也加载进去。但如果 Apache Active MQ使用了 SLF4J,你可以继续使用你的日志类库而无语忍受加载和维护一个新的日志框架的痛苦。

从设计模式的角度考虑,它是用来在 log和代码层之间起到门面的作用。对用户来说只要使用 slf4j提供的接口,即可隐藏日志的具体实现。这与 jdbc和相似。使用 jdbc也就避免了不同的具体数据库。使用了 slf4j可以对客户端应用解耦。因为当我们在代码实现中引入 log日志的时候,用的是接口,所以可以实时的更具情况来调换具体的日志实现类。这就是 slf4j的作用。

统一日志框架

用法就是和整合单元测试框架那样,把启动器里其它依赖的日志框架剔除掉,然后统一使用一个日志框架(因为都是实现的 SLF4J 接口,如下图)

image.png

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-logging -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>

SpringBoot 能自动适配所有的日志,而且底层使用 slf4j + logback 的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可

<!-- 如下,检测 maven 依赖,查看依赖关系图,然后剔除掉相应的框架就好了 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

可以看到实际上最后所有的 日志依赖都到 logback 这个日志框架上了(logback 是 log4j 的升级版)

image.png

一般无需手动添加 spring-boot-starter-logging 日志框架,当使用了 spring-boot-starter-web 时,它会自动把这个引入进去(它们之间有隐式的依赖关系)

配置日志

注:这里只做基本的介绍,详细的看 LOG4J 那篇笔记,配置项都是差不多的

主要使用的是 Lombok 的 @Slf4j 注解,自动注入一个 logging 进来

@Slf4j
public class Temp {
@Override
public void hello() {
// 这个 log 是上面那个 @Slf4j 注解注入的(Lombok插件)
log.debug("这是 注入进来的 logging");
}
}

每个Logger都被了一个日志级别(log level),用来控制日志信息的输出。日志级别从高到低分为:

  • OFF 最高等级,用于关闭所有日志记录。
  • FATAL 严重错误,不会影响系统运行
  • ERROR 错误信息,指出虽然发生错误事件,但仍然不影响系统的继续运行。
  • WARN 警告信息,表明会出现潜在的错误情形。
  • INFO 运行信息,记录数据连接、网络连接、IO操作等等
  • DEBUG 调试信息,一般用在开发中使用,记录程序变量参数传递信息等等
  • trace 追踪信息,记录程序所有的流程信息
  • ALL 最低等级,用于打开所有日志记录。

这里可以单独为不同的包设置不同的日志等级

# 配置日志级别
logging:
level:
com.alsritter: debug
org.springframework: info
file:
path: ./var/log #日志路径(日志文件名自动生成)

扩展配置

参考资料 SpringBoot系列——Logback日志,输出到文件以及实时输出到web页面

Spring Boot包含许多Logback扩展,可以帮助进行高级配置。可以在 logback-spring.xml 配置文件中使用这些扩展。如果需要比较复杂的配置,建议使用扩展配置的方式

PS:SpringBoot 推荐使用带 -spring 后缀的 logback-spring.xml 扩展配置,因为默认的的 logback.xml 标准配置, Spring 无法完全控制日志初始化。(spring 扩展对 springProfile 节点的支持)

以下是项目常见的完整 logback-spring.xml,SpringBoot 默认扫描 classpath下面的 logback.xmllogback-spring.xml,所以不需要显示的指定 spring.logging.config 当然,指定也没有问题

logging:
config: classpath:logback-spring.xml

。配置示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--日志文件主目录:这里${user.home}为当前服务器用户主目录-->
<property name="LOG_HOME" value="${user.home}/log"/>
<!--日志文件名称:这里spring.application.name表示工程名称-->
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>

<!--默认配置-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--配置控制台(Console)-->
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

<!--配置日志文件(File)-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--设置策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件路径:这里%d{yyyyMMdd}表示按天分类日志-->
<FileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</FileNamePattern>
<!--日志保留天数-->
<MaxHistory>15</MaxHistory>
</rollingPolicy>
<!--设置格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<!-- 或者使用默认配置 -->
<!--<pattern>${FILE_LOG_PATTERN}</pattern>-->
<charset>utf8</charset>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
</appender>

<!-- 多环境配置 按照active profile选择分支 -->
<springProfile name="dev">
<!--root节点 全局日志级别,用来指定最基础的日志输出级别-->
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</root>

<!-- 子节点向上级传递 局部日志级别-->
<logger level="WARN" name="org.springframework"/>
<logger level="WARN" name="com.netflix"/>
<logger level="DEBUG" name="org.hibernate.SQL"/>
</springProfile>
<springProfile name="prod">

</springProfile>
</configuration>